<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import type { TableInstance, FormInstance, FormRules, UploadInstance, CheckboxValueType, UploadRequestOptions } from 'element-plus'
import draggable from 'vuedraggable'
import { useI18n } from 'vue-i18n'
import DialogView from 'components/DialogView.vue'
import CodeRender from 'components/CodeRender.vue'
import { useUserStore } from 'stores/user-store'
import {
  retrieveSchemas, retrieveSchemaFields, retrieveSchemaPreview, fetchSchema, createSchema, modifySchema,
  removeSchema, configSchemaFields, generateSchema, syncSchema, checkSchemaExists, importSchemas
} from 'src/api/schemas'
import { retrieveDictionarySubset } from 'src/api/dictionaries'
import { retrieveTables } from 'src/api/db'
import { retrieveTemplates } from 'src/api/templates'
import type { Pagination, Dictionary, Schema, Field, Template, TableInfo } from 'src/types'
import { visibleArray, pluralToSingularAndCapitalize, groupByType, downloadFile, hasAction } from 'src/utils'
import { Icon } from '@iconify/vue'


const { t } = useI18n()
const userStore = useUserStore()

const loading = ref<boolean>(false)
const saveLoading = ref<boolean>(false)
const importLoading = ref<boolean>(false)
const exportLoading = ref<boolean>(false)
const syncLoading = ref<boolean>(false)
const configLoading = ref<boolean>(false)
const executeLoading = ref<Record<number, boolean>>({})

const datas = ref<Array<Schema>>([])
const renderedTemplates = ref<Array<Template>>([])
const total = ref<number>(0)

const tableRef = ref<TableInstance>()
const pagination = reactive<Pagination>({
  page: 1,
  size: 10
})

const tables = ref<Array<TableInfo>>([])
const fields = ref<Array<Field>>([])
const templates = ref<Array<Template>>([])
const templateTypeOptions = ref<Array<Dictionary>>([])

const checkAll = ref<boolean>(true)
const isIndeterminate = ref<boolean>(false)
const checkedColumns = ref<Array<string>>(['name', 'description'])
const columns = ref<Array<string>>(['name', 'description'])

const visible = ref<boolean>(false)
const configVisible = ref<boolean>(false)
const previewVisible = ref<boolean>(false)
const importVisible = ref<boolean>(false)

const currentRowId = ref<number | null>(null)

const importRef = ref<UploadInstance>()
const formRef = ref<FormInstance>()

const filters = ref({
  name: null
})

const initialValues: Schema = {
  id: undefined,
  name: '',
  comment: '',
  reference: '',
  domain: '',
  templates: []
}
const form = ref<Schema>({ ...initialValues })

const configFormRef = ref<FormInstance>()

const rules = reactive<FormRules<typeof form>>({
  name: [
    { required: true, message: t('inputText', { field: t('name') }), trigger: 'blur' },
    { validator: checkNameExistsence, trigger: 'blur' }
  ],
  reference: [
    { required: true, message: t('inputText', { field: t('reference') }), trigger: 'blur' }
  ],
  domain: [
    { required: true, message: t('inputText', { field: t('domain') }), trigger: 'blur' }
  ]
})

function checkNameExistsence(rule: unknown, value: string, callback: (error?: string | Error) => void) {
  checkSchemaExists(value, form.value.id).then(res => {
    if (res.data) {
      callback(new Error(t('alreadyExists', { field: t('name') })))
    } else {
      callback()
    }
  })
}

const fieldTypeOptions = ref<Array<Dictionary>>([])
const formTypeOptions = ref<Array<Dictionary>>([])

/**
 * 分页变化
 * @param currentPage 当前页码
 * @param pageSize 分页大小
 */
function pageChange(currentPage: number, pageSize: number) {
  pagination.page = currentPage
  pagination.size = pageSize
  load()
}

/**
 * 加载列表
 */
async function load() {
  loading.value = true
  retrieveSchemas(pagination, filters.value).then(res => {
    datas.value = res.data.content
    total.value = res.data.page.totalElements
  }).finally(() => { loading.value = false })
}

async function loadTemplates() {
  retrieveTemplates({ page: 1, size: 99 }).then(res => {
    templates.value = res.data.content
  })
}

async function loadTemplateType() {
  retrieveDictionarySubset(25).then(res => {
    templateTypeOptions.value = res.data
  })
}

/**
   * reset
   */
function reset() {
  filters.value = {
    name: null
  }
  load()
}

onMounted(() => {
  load()
  loadTemplates()
  loadTemplateType()
})

/**
   * 导入
   */
function importRows() {
  importVisible.value = true
}

/**
   * 导出
   */
async function exportRows() {
  exportLoading.value = true

  const selectedRows = tableRef.value?.getSelectionRows()
  if (selectedRows) {
    console.log('selectedRows: ', selectedRows)
  }

}

/**
   * format templates
   * @param cellValue cell value
   */
function formatTemplates(cellValue: number): string {
  const templateItem = templates.value.find(item => item.id === cellValue)
  return templateItem ? templateItem.name + templateItem.suffix : ''
}

/**
   * config
   * @param id 主键
   */
function configRow(id: number) {
  configVisible.value = true
  currentRowId.value = id
  loadFields(id)
}

async function loadFields(id: number) {
  configLoading.value = true
  const [fieldTypeRes, formTypeRes, schemaFieldsRes] = await Promise.all([
    retrieveDictionarySubset(1),
    retrieveDictionarySubset(13),
    retrieveSchemaFields(id)])
  fieldTypeOptions.value = fieldTypeRes.data
  formTypeOptions.value = formTypeRes.data
  fields.value = schemaFieldsRes.data

  configLoading.value = false
}

/**
   * 弹出框
   * @param id 主键
   */
async function saveRow(id?: number) {
  form.value = { ...initialValues }
  if (id) {
    loadOne(id)
  }
  retrieveTables().then(res => {
    tables.value = res.data
  })

  visible.value = true
}

async function syncRows() {
  const selectedRows = tableRef.value?.getSelectionRows()
  const ids: number[] = selectedRows.map((item: Template) => item.id)
  if (!ids.length) {
    ElMessage({
      message: t('noDataSelected'),
      type: 'warning',
      plain: true
    })
  } else {
    syncLoading.value = true
    syncSchema(ids).finally(() => {
      syncLoading.value = false
    })
  }

}

/**
 * preview
 * @param id 主键
 */
async function previewRow(id: number) {
  previewVisible.value = true
  retrieveSchemaPreview(id).then(res => {
    renderedTemplates.value = res.data
  })
}

/**
 * execute and download
 * @param id Row ID
 * @param name Row name
 */
async function executeRow(id: number, name: string) {
  executeLoading.value[id] = true
  generateSchema(id).then(res => {
    const mimeType = 'application/zip'
    downloadFile(res.data, name || 'codes' + '.zip', mimeType)
  }).finally(() => {
    executeLoading.value[id] = false
  })
}

/**
   * 加载
   * @param id 主键
   */
async function loadOne(id: number) {
  fetchSchema(id).then(res => {
    form.value = res.data
  })
}

/**
   * 表单提交
   */
async function onSubmit(formEl: FormInstance | undefined) {
  if (!formEl) return

  formEl.validate((valid) => {
    if (valid) {
      saveLoading.value = true
      if (form.value.id) {
        modifySchema(form.value.id, form.value).then(() => {
          load()
          visible.value = false
        }).finally(() => { saveLoading.value = false })
      } else {
        createSchema(form.value).then(() => {
          load()
          visible.value = false
        }).finally(() => { saveLoading.value = false })
      }
    }
  })
}

/**
   * 表单提交
   */
async function onImportSubmit(importEl: UploadInstance | undefined) {
  if (!importEl) return
  importLoading.value = true

  importEl.submit()

  importLoading.value = false
  importVisible.value = false
}

function onUpload(options: UploadRequestOptions) {
  return importSchemas(options.file)
}

/**
   * 删除
   * @param id 主键
   */
function removeRow(id: number) {
  removeSchema(id).then(() => {
    load()
  })
}

/**
   * config submit
   */
async function configSubmit(configFormEl: FormInstance | undefined) {
  if (!configFormEl) return
  configFormEl.validate((valid) => {
    if (valid) {
      configLoading.value = true
      if (currentRowId.value) {
        configSchemaFields(currentRowId.value, fields.value).then(res => {
          fields.value = res.data
          configVisible.value = false
          load()
        }).finally(() => { configLoading.value = false })
      }
    }
  })
}

/**
   * 确认
   * @param id 主键
   */
function confirmEvent(id: number) {
  if (id) {
    removeRow(id)
  }
}

function onSelectChange(value: string) {
  if (value && value.length > 0) {
    const domain = pluralToSingularAndCapitalize(value)
    form.value.domain = domain

    const table = tables.value.find(item => item.tableName === value)
    form.value.comment = table?.tableComment || ''
  }
}

/**
   * 全选操作
   * @param val 是否全选
   */
function handleCheckAllChange(val: CheckboxValueType) {
  checkedColumns.value = val ? columns.value : []
  isIndeterminate.value = false
}

/**
   * 选中操作
   * @param value 选中的值
   */
function handleCheckedChange(value: CheckboxValueType[]) {
  const checkedCount = value.length
  checkAll.value = checkedCount === columns.value.length
  isIndeterminate.value = checkedCount > 0 && checkedCount < columns.value.length
}
</script>

<template>
  <ElSpace size="large" fill>
    <ElCard shadow="never">
      <ElForm inline :model="filters" @submit.prevent>
        <ElFormItem :label="$t('name')" prop="name">
          <ElInput v-model="filters.name" :placeholder="$t('inputText', { field: $t('name') })" />
        </ElFormItem>
        <ElFormItem>
          <ElButton title="search" type="primary" @click="load">
            <Icon icon="material-symbols:search-rounded" width="18" height="18" />{{ $t('search') }}
          </ElButton>
          <ElButton title="reset" @click="reset">
            <Icon icon="material-symbols:replay-rounded" width="18" height="18" />{{ $t('reset') }}
          </ElButton>
        </ElFormItem>
      </ElForm>
    </ElCard>

    <ElCard shadow="never">
      <ElRow :gutter="20" justify="space-between" class="mb-4">
        <ElCol :span="16" class="text-left">
          <ElButton v-if="hasAction($route.name, 'create')" title="create" type="primary" @click="saveRow()">
            <Icon icon="material-symbols:add-rounded" width="18" height="18" />{{ $t('create') }}
          </ElButton>
          <ElButton v-if="hasAction($route.name, 'sync')" title="sync" type="default" plain @click="syncRows">
            <Icon icon="material-symbols:sync-rounded" width="16" height="16" :class="[syncLoading ? 'spin' : '']" />
            {{ $t('sync') }}
          </ElButton>
          <ElButton v-if="hasAction($route.name, 'import')" title="import" type="warning" plain @click="importRows"
            :loading="importLoading">
            <Icon icon="material-symbols:database-upload-outline-rounded" width="18" height="18" />{{ $t('import') }}
          </ElButton>
          <ElButton v-if="hasAction($route.name, 'export')" title="export" type="success" plain @click="exportRows"
            :loading="exportLoading">
            <Icon icon="material-symbols:file-export-outline-rounded" width="18" height="18" />{{ $t('export') }}
          </ElButton>
        </ElCol>

        <ElCol :span="8" class="text-right">
          <ElTooltip class="box-item" effect="dark" :content="$t('refresh')" placement="top">
            <ElButton title="refresh" type="primary" plain circle @click="load">
              <Icon icon="material-symbols:refresh-rounded" width="18" height="18" />
            </ElButton>
          </ElTooltip>

          <ElTooltip :content="$t('column') + $t('settings')" placement="top">
            <span class="inline-block ml-3 h-8">
              <ElPopover :width="200" trigger="click">
                <template #reference>
                  <ElButton title="settings" type="success" plain circle>
                    <Icon icon="material-symbols:format-list-bulleted" width="18" height="18" />
                  </ElButton>
                </template>
                <div>
                  <ElCheckbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
                    {{ $t('all') }}
                  </ElCheckbox>
                  <ElDivider />
                  <ElCheckboxGroup v-model="checkedColumns" @change="handleCheckedChange">
                    <draggable v-model="columns" item-key="simple">
                      <template #item="{ element }">
                        <div class="flex items-center space-x-2">
                          <Icon icon="material-symbols:drag-indicator" width="18" height="18"
                            class="hover:cursor-move" />
                          <ElCheckbox :label="element" :value="element" :disabled="element === columns[0]">
                            <div class="inline-flex items-center space-x-4">
                              {{ $t(element) }}
                            </div>
                          </ElCheckbox>
                        </div>
                      </template>
                    </draggable>
                  </ElCheckboxGroup>
                </div>
              </ElPopover>
            </span>
          </ElTooltip>
        </ElCol>
      </ElRow>

      <ElTable ref="tableRef" v-loading="loading" :data="datas" row-key="id" stripe table-layout="auto">
        <ElTableColumn type="selection" width="55" />
        <ElTableColumn type="index" :label="$t('no')" width="55" />
        <ElTableColumn prop="name" :label="$t('name')">
          <template #default="scope">
            <ElButton title="preview" v-if="scope.row.enabled && scope.row.templates && scope.row.templates.length > 0"
              type="primary" link @click="previewRow(scope.row.id)">
              {{ scope.row.name }}
            </ElButton>
          </template>
        </ElTableColumn>
        <ElTableColumn prop="comment" :label="$t('comment')" />
        <ElTableColumn show-overflow-tooltip prop="reference" :label="$t('reference')" />
        <ElTableColumn prop="domain" :label="$t('domain')" />
        <ElTableColumn prop="templates" :label="$t('templates')">
          <template #default="scope">
            <template v-if="scope.row.templates && scope.row.templates.length > 0">
              <ElTag v-for="(item, index) in visibleArray(scope.row.templates, 1)" :key="index" type="primary"
                class="mr-2">
                {{ formatTemplates(item as number) }}
              </ElTag>
              <ElPopover v-if="scope.row.templates.length > 1" placement="top-start" trigger="hover">
                <template #reference>
                  <ElTag type="primary">
                    +{{ scope.row.templates.length - 1 }}
                  </ElTag>
                </template>
                <ElTag v-for="(item, index) in scope.row.templates.slice(1)" :key="index" type="primary"
                  class="mb-2 mr-2">
                  {{ formatTemplates(item) }}
                </ElTag>
              </ElPopover>
            </template>
          </template>
        </ElTableColumn>
        <ElTableColumn :label="$t('actions')">
          <template #default="scope">
            <ElButton v-if="hasAction($route.name, 'modify')" title="modify" size="small" type="primary" link
              @click="saveRow(scope.row.id)">
              <Icon icon="material-symbols:edit-outline-rounded" width="16" height="16" />{{ $t('modify') }}
            </ElButton>
            <ElButton v-if="hasAction($route.name, 'config')" title="config" size="small" type="success" link
              @click="configRow(scope.row.id)">
              <Icon icon="material-symbols:rule-settings-rounded" width="16" height="16" />{{ $t('config') }}
            </ElButton>
            <ElPopconfirm :title="$t('removeConfirm')" :width="240" @confirm="confirmEvent(scope.row.id)">
              <template #reference>
                <ElButton v-if="hasAction($route.name, 'remove')" title="remove" size="small" type="danger" link>
                  <Icon icon="material-symbols:delete-outline-rounded" width="16" height="16" />{{ $t('remove') }}
                </ElButton>
              </template>
            </ElPopconfirm>

            <ElButton title="execute"
              v-if="hasAction($route.name, 'execute') && scope.row.enabled && scope.row.templates && scope.row.templates.length > 0"
              size="small" type="warning" link @click="executeRow(scope.row.id, scope.row.name)">
              <Icon
                :icon="`material-symbols:${executeLoading[scope.row.id] ? 'motion-photos-on-outline-rounded' : 'motion-play-outline-rounded'}`"
                width="16" height="16" :class="[executeLoading[scope.row.id] ? 'spin' : '']" />
              {{ $t('execute') }}
            </ElButton>

          </template>
        </ElTableColumn>
      </ElTable>
      <ElPagination layout="prev, pager, next, sizes, jumper, ->, total" @change="pageChange" :total="total" />
    </ElCard>
  </ElSpace>

  <!-- form -->
  <DialogView v-model="visible" :title="$t('generators')" width="35%">
    <ElForm ref="formRef" :model="form" :rules="rules" label-position="top">
      <ElRow :gutter="20" class="w-full !mx-0">
        <ElCol :span="12">
          <ElFormItem :label="$t('name')" prop="name">
            <ElSelect v-model="form.name" @change="onSelectChange">
              <ElOption v-for="(item, index) in tables" :key="index" :value="item.tableName"
                :label="`${item.tableName} (${item.tableComment})`" />
            </ElSelect>
          </ElFormItem>
        </ElCol>
        <ElCol :span="12">
          <ElFormItem :label="$t('comment')" prop="comment">
            <ElInput v-model="form.comment" :placeholder="$t('inputText', { field: $t('comment') })" />
          </ElFormItem>
        </ElCol>
      </ElRow>
      <ElRow :gutter="20" class="w-full !mx-0">
        <ElCol :span="12">
          <ElFormItem :label="$t('domain')" prop="domain">
            <ElInput v-model="form.domain" :placeholder="$t('inputText', { field: $t('domain') })" disabled />
          </ElFormItem>
        </ElCol>
        <ElCol :span="12">
          <ElFormItem :label="$t('reference')" prop="reference">
            <ElInput v-model="form.reference" :placeholder="$t('inputText', { field: $t('reference') })" />
          </ElFormItem>
        </ElCol>
      </ElRow>
      <ElRow :gutter="20" class="w-full !mx-0">
        <ElCol>
          <ElFormItem :label="$t('templates')" prop="templates">
            <ElSelect v-model="form.templates" multiple>
              <ElOptionGroup v-for="(items, group) in groupByType(templates, templateTypeOptions, 'type')" :key="group"
                :label="String(group)">
                <ElOption v-for="item in items" :key="item.id" :value="item.id!"
                  :label="`${item.name}${item.suffix} (${item.version})`" />
              </ElOptionGroup>
            </ElSelect>
          </ElFormItem>
        </ElCol>
      </ElRow>
    </ElForm>
    <template #footer>
      <ElButton title="cancel" @click="visible = false">
        <Icon icon="material-symbols:close" width="18" height="18" />{{ $t('cancel') }}
      </ElButton>
      <ElButton title="submit" type="primary" :loading="saveLoading" @click="onSubmit(formRef)">
        <Icon icon="material-symbols:check-circle-outline-rounded" width="18" height="18" /> {{ $t('submit') }}
      </ElButton>
    </template>
  </DialogView>

  <!-- config -->
  <DialogView v-model="configVisible" :title="$t('config')" width="85%" :max-height="450" :show-full-screen="true">
    <ElForm ref="configFormRef" :model="{ fields }" :show-message="false">
      <ElTable v-loading="configLoading" :data="fields" row-key="id" stripe table-layout="auto">
        <ElTableColumn type="index" :label="$t('no')" width="55" />
        <ElTableColumn prop="name" :label="$t('name')" />
        <ElTableColumn prop="dataType" :label="$t('type')" />
        <ElTableColumn prop="length" :label="$t('length')" />
        <ElTableColumn prop="nullable" :label="$t('nullable')">
          <template #default="scope">
            <ElBadge is-dot :type="scope.row.nullable ? 'info' : 'primary'" class="mr-1" />
            {{ scope.row.nullable ? 'Y' : 'N' }}
          </template>
        </ElTableColumn>
        <ElTableColumn show-overflow-tooltip prop="comment" :label="$t('comment')" />
        <ElTableColumn prop="fieldType" :label="$t('fieldType')">
          <template #default="scope">
            <ElFormItem :prop="'fields.' + scope.$index + '.fieldType'" :rules="[{ required: true, trigger: 'blur' }]">
              <ElSelect v-model="scope.row.fieldType" class="min-w-20">
                <ElOption v-for="item in fieldTypeOptions" :key="item.id" :label="item.name" :value="item.name" />
              </ElSelect>
            </ElFormItem>
          </template>
        </ElTableColumn>
        <ElTableColumn prop="formType" :label="$t('formType')">
          <template #default="scope">
            <ElFormItem :prop="'fields.' + scope.$index + '.formType'" :rules="[{ required: true, trigger: 'blur' }]">
              <ElSelect v-model="scope.row.formType" class="min-w-28">
                <ElOption v-for="item in formTypeOptions" :key="item.id" :label="item.name" :value="item.name" />
              </ElSelect>
            </ElFormItem>
          </template>
        </ElTableColumn>
        <ElTableColumn prop="queryable" :label="$t('queryable')">
          <template #default="scope">
            <ElCheckbox v-model="scope.row.queryable" />
          </template>
        </ElTableColumn>
        <ElTableColumn prop="editable" :label="$t('editable')">
          <template #default="scope">
            <ElFormItem prop="editable">
              <ElCheckbox v-model="scope.row.editable" />
            </ElFormItem>
          </template>
        </ElTableColumn>
      </ElTable>
    </ElForm>
    <template #footer>
      <ElButton title="cancel" @click="configVisible = false">
        <Icon icon="material-symbols:close" width="18" height="18" />{{ $t('cancel') }}
      </ElButton>
      <ElButton title="submit" type="primary" native-type="submit" :loading="configLoading"
        @click="configSubmit(configFormRef)">
        <Icon icon="material-symbols:check-circle-outline-rounded" width="18" height="18" /> {{ $t('submit') }}
      </ElButton>
    </template>
  </DialogView>

  <!-- preview -->
  <DialogView v-model="previewVisible" show-close :title="$t('preview')" width="50%" :max-height="600"
    :show-full-screen="true">
    <ElTabs stretch>
      <ElTabPane v-for="item in renderedTemplates" :key="item.name" :label="item.name + item.suffix">
        <CodeRender :content="item.content" />
      </ElTabPane>
    </ElTabs>
  </DialogView>

  <!-- import -->
  <DialogView v-model="importVisible" :title="$t('import')" width="36%">
    <p>{{ $t('templates') + ' ' + $t('download') }}：
      <a :href="`templates/generators.xlsx`" :download="$t('generators') + '.xlsx'">
        {{ $t('generators') }}.xlsx
      </a>
    </p>
    <ElUpload ref="importRef" :limit="1" drag :auto-upload="false" :http-request="onUpload" :on-success="load"
      accept=".xls,application/vnd.ms-excel,.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
      :headers="{ Authorization: `Bearer ${userStore.accessToken}` }">
      <div class="el-icon--upload inline-flex justify-center">
        <Icon icon="material-symbols:upload-rounded" width="48" height="48" />
      </div>
      <div class="el-upload__text">
        {{ $t('drop2Here') }}<em>{{ $t('click2Upload') }}</em>
      </div>
      <template #tip>
        <div class="el-upload__tip">
          {{ $t('fileSizeLimit', { size: '50MB' }) }}
        </div>
      </template>
    </ElUpload>
    <p class="text-red">error:</p>
    <template #footer>
      <ElButton title="cancel" @click="importVisible = false">
        <Icon icon="material-symbols:close" width="18" height="18" />{{ $t('cancel') }}
      </ElButton>
      <ElButton title="submit" type="primary" :loading="importLoading" @click="onImportSubmit(importRef)">
        <Icon icon="material-symbols:check-circle-outline-rounded" width="18" height="18" /> {{ $t('submit') }}
      </ElButton>
    </template>
  </DialogView>
</template>

<style lang="scss" scoped>
.el-badge {
  display: inline-flex;
  vertical-align: baseline;
}

.cell {
  .el-form-item {
    margin-bottom: 0px;
  }
}

.el-dropdown {
  vertical-align: middle;
  padding: 2px;
  margin-left: 8px;
}
</style>
